import 'package:tflite_v2/tflite_v2.dart';
import 'package:camera/camera.dart';
import 'dart:typed_data';
import 'package:image/image.dart' as img;
import 'package:logging/logging.dart';

// TFLiteService 类管理TFLite模型的加载和执行
class TFLiteService {
  final _logger = Logger('TFLiteService');
  bool _isModelLoaded = false; // 用于标识模型是否已加载
  List<int> _inputShape = [1, 224, 224, 3]; // 假定模型输入尺寸为224x224 RGB

  // 加载模型和标签
  Future<void> loadModel() async {
    try {
      String? res = await Tflite.loadModel(
        model: "assets/models/multi_person_mobilenet_v1_075_float.tflite",
        labels: "assets/models/pose_labels.txt",
      );
      _isModelLoaded = true;
      _logger.info("Model loaded successfully: $res");
      _inputShape = [1, 224, 224, 3]; // 通过TFLite API获取正确的输入尺寸，此处为示例
    } catch (e) {
      _logger.severe("Failed to load model: $e"); // 捕捉并打印加载模型时的错误
      _isModelLoaded = false;
    }
  }

  // 对摄像头捕获的图像执行姿态估计
  Future<List<dynamic>?> runPoseEstimation(CameraImage image) async {
    if (!_isModelLoaded) {
      _logger.warning("Model not loaded yet.");
      return null;
    }

    try {
      // 预处理图像
      Uint8List imageBytes = _preprocessCameraImage(image);

      // 执行模型推理并获取原始输出
      var rawResults = await Tflite.runModelOnBinary(
        binary: imageBytes.buffer.asUint8List(),
        numResults: 1,
        threshold: 0.1,
      );

      // 打印原始结果以便调试
      _logger.info('TFLite raw results: $rawResults');

      // 处理结果为标准格式
      if (rawResults != null) {
        return rawResults
            .map((result) => {
                  'x': (result['x'] as num).toDouble(),
                  'y': (result['y'] as num).toDouble(),
                })
            .toList();
      }

      return null;
    } catch (e, stackTrace) {
      _logger.severe('TFLite inference error', e, stackTrace);
      return null;
    }
  }

  // 图像预处理函数
  Uint8List _preprocessCameraImage(CameraImage cameraImage) {
    // 获取目标高度和宽度
    // int height = inputShape[1];
    // int width = inputShape[2];
    // var convertedBytes = Float32List(height * width * 3);
    // var buffer = Float32List.view(convertedBytes.buffer);
    // int pixelIndex = 0;
    //
    // // 此处仅为示例，根据您的具体图像格式进行调整（例如YUV420、RGBA等）
    // for (int i = 0; i < height; i++) {
    //   for (int j = 0; j < width; j++) {
    //     final int pixel = image.planes[0].bytes[i * width + j];  // 假设图像格式为单通道灰度图
    //     buffer[pixelIndex++] = (pixel & 0xFF) / 255.0;
    //   }
    // }
    //
    // return convertedBytes.buffer.asUint8List();

    // 将CameraImage转换为image库的Image格式
    img.Image image = convertYUV420toImageColor(cameraImage);

    // 调整图像尺寸到模型输入尺寸
    img.Image resized =
        img.copyResize(image, width: _inputShape[2], height: _inputShape[1]);

    // 转换图像为ByteData供TensorFlow Lite使用
    Uint8List bytes = Uint8List.fromList(img.encodeJpg(resized));
    return bytes;
  }

  // 将摄像头的YUV420格式图像转换为RGB格式的图像
  img.Image convertYUV420toImageColor(CameraImage cameraImage) {
    // 获取图像的宽度和高度
    final int width = cameraImage.width;
    final int height = cameraImage.height;

    // 分别获取YUV三个平面
    final yPlane = cameraImage.planes[0];
    final uPlane = cameraImage.planes[1];
    final vPlane = cameraImage.planes[2];

    // 创建一个新的RGB格式的图像对象
    img.Image image = img.Image(width: width, height: height);

    // 遍历每一个像素点进行YUV到RGB的转换
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        // 计算当前像素对应UV分量的索引
        final int uvPos = uvIndex(x, y, width);
        // 计算当前像素的线性索引
        final int index = y * width + x;
        // 分别获取Y, U, V值
        final Y = yPlane.bytes[index];
        final U = uPlane.bytes[uvPos];
        final V = vPlane.bytes[uvPos];

        // 根据YUV到RGB的转换公式进行计算
        var R = Y + V * 1.4075;
        var G = Y - U * 0.3455 - V * 0.7169;
        var B = Y + U * 1.779;

        // 限制RGB值在0-255范围内
        R = R.clamp(0, 255).toInt() as double;
        G = G.clamp(0, 255).toInt() as double;
        B = B.clamp(0, 255).toInt() as double;

        // 设置图像的像素值
        image.setPixelRgba(x, y, R.toInt(), G.toInt(), B.toInt(), 255);
      }
    }

    return image;
  }

// 辅助函数，用于计算UV分量在内存中的位置
  int uvIndex(int x, int y, int width) {
    // 由于U和V平面是每两个像素采样一次，因此需要调整索引计算方法
    return (x ~/ 2) + (y ~/ 2) * (width ~/ 2);
  }

  // 释放模型资源
  void dispose() {
    if (_isModelLoaded) {
      Tflite.close();
    }
  }
}
